home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Networking / OTStreamLogViewer1.0b1 / IC Libraries / ICCommonSubs.p < prev    next >
Encoding:
Text File  |  1998-03-15  |  42.9 KB  |  1,400 lines  |  [TEXT/CWIE]

  1. unit ICCommonSubs;
  2.  
  3. (*    This file is part of the Internet Configuration system and is placed in the public domain for the benefit of all.
  4.  
  5.     This file holds all those miscellaneous little functions that are basically wrappers
  6.     around existing OS functionality.
  7. *)
  8.  
  9. interface
  10.  
  11.     uses
  12.         Files,
  13.         Windows,
  14.         Lists, 
  15.         AppleEvents,
  16.         Menus,
  17.         
  18.         InternetConfig;
  19.  
  20.     (* ***** Event Manager Stuff ***** *)
  21.  
  22.     (* A collection of useful ASCII character definitions. *)            
  23.     const
  24.         kNulChar            = chr($00);
  25.         kHomeChar             = chr($01);
  26.         kEnterChar             = chr($03);
  27.         kEndChar             = chr($04);
  28.         kHelpChar             = chr($05);
  29.         kBackSpaceChar         = chr($08);
  30.         kTabChar             = chr($09);
  31.         kLineFeedChar         = chr($0A);
  32.         kPageUpChar         = chr($0B);
  33.         kPageDownChar         = chr($0C);
  34.         kCRChar             = chr($0D);
  35.         kEscChar             = chr($1B);
  36.         kClearChar             = chr($1B);
  37.         kLeftArrowChar         = chr($1C);
  38.         kRightArrowChar     = chr($1D);
  39.         kUpArrowChar         = chr($1E);
  40.         kDownArrowChar        = chr($1F);
  41.         kSpaceChar             = chr($20);
  42.         kDelChar             = chr($7F);
  43.         kBulletChar         = chr($A5);
  44.  
  45.     (* A collection of useful virtual key code definitions. *)            
  46.     const
  47.         kUndoKeyCode         = 122;
  48.         kCutKeyCode         = 120;
  49.         kCopyKeyCode         = 99;
  50.         kPasteKeyCode         = 118;
  51.         kClearKeyCode         = 71;
  52.  
  53.         kEscKeyCode         = 53;
  54.         kReturnKeyCode         = 36;
  55.         kEnterKeyCode         = 52;
  56.         kTabKeyCode         = 48;
  57.         kSpaceKeyCode        = 49;
  58.         KDeleteKeyCode         = 51;
  59.  
  60.         kCommandKeyCode     = 55;
  61.         kShiftKeyCode         = 56;
  62.         kCapsLockKeyCode     = 57;
  63.         kOptionKeyCode         = 58;
  64.  
  65.     (* ***** Memory Stuff ***** *)
  66.     
  67.     const
  68.         kHandleLockBit = 7;
  69.         kHandlePurgeBit = 6;
  70.         kHandleResourceBit = 5;
  71.         
  72.         kHandleLockMask = $80;
  73.         kHandlePurgeMask = $40;
  74.         kHandleResourceMask = $20;
  75.         
  76.     type
  77.         (* A data structure for addresses memory as bytes. *)
  78.         BigBuffer = packed array [0..$0FFFFFF] of Byte;
  79.         BigBufferPtr = ^BigBuffer;
  80.         BigBufferHandle = ^BigBufferPtr;
  81.  
  82.         (* Another for addressing memory as chars. *)
  83.         BigCharArray = packed array [0..$FFFFFF] of char;
  84.         BigCharArrayPtr = ^BigCharArray;
  85.         BigCharArrayHandle = ^BigCharArrayPtr;
  86.  
  87.     function BlockCompare (lhsBaseAddr : univ Ptr; rhsBaseAddr: univ Ptr; size: longint): Boolean;
  88.         (* Compares two blocks of memory for equality.*)
  89.         
  90.     procedure BlockFill (baseAddr: univ Ptr; size: longint; value: integer);
  91.         (* Fills a block of memory with the value.  The memory is filled
  92.             as bytes, ie the high byte of value is ignored.
  93.         *)
  94.  
  95.     (* ***** Resource Manager Stuff ***** *)
  96.  
  97.     function CheckMemError(memoryHandle : univ Handle) : ICError;
  98.     function CheckResError(resourceHandle : univ Handle) : ICError;
  99.  
  100.     function AddNamedResource(data : Handle; theType : ResType; const name : Str255) : ICError;
  101.         (*    Adds data to the current resource file as a resource
  102.             of type theType with the given name.  It calculates a
  103.             unique resource ID for the newly added resource.  Note that,
  104.             like AddResource, data comes back as either a resource
  105.             handle (on noErr) or still a memory handle (on error).
  106.         *)
  107.         
  108.     function Set1Resource(theData : Handle; theType : ResType; theID : integer) : OSStatus;
  109.         (*    This routine sets the resource denoted by theType and theID to contain
  110.             theData.  If the resource does not currently exist, it is created.
  111.             If it does currently exist, it is modified.  theData is not disposed
  112.             of.
  113.         *)
  114.     function Set1ResourcePtr(theData : univ Ptr; theDataSize : longint; theType : ResType; theID : integer) : OSStatus;
  115.         (*    Works like Set1Resource except that you pass in a pointer and size. *)
  116.     
  117.     function Set1NamedResource(theData : Handle; theType : ResType; const name : Str255) : OSStatus;
  118.         (*    This routine sets the resource denoted by theType and name to contain
  119.             theData.  If the resource does not currently exist, it is created with
  120.             a unique ID greater than 127.  If it does currently exist, it is modified.
  121.             theData is not disposed of.
  122.         *)
  123.  
  124.     (* ***** File Manager Stuff ***** *)
  125.     
  126.     function GetVolInfo (var ioName: Str63; var ioVRefNum: integer; ioVolIndex: integer;
  127.                                         var ioVCrDate: longint): OSStatus;
  128.         (* Returns information about the specified volume. Basically this is a wrapper
  129.             around PBGetVInfo.  See IM:Files for a description of the meaning of the
  130.             ioName, ioVRefNum and ioVolIndex parameters.  This routine also returns
  131.             the volume's creation date in ioVCrDate to aid in Poor Man's Alias Manager
  132.             volume matching.
  133.         *)
  134.         
  135.     function FindApplicationInDTDB (creator: OSType; var foundApplicationSpec: FSSpec): OSStatus;
  136.         (* This routine attempts to find an application in the desktop database
  137.             given its creator type.
  138.         *)
  139.         
  140.     function FSpGetCatInfo (var fss: FSSpec; ioFDirIndex: integer; var cpb: CInfoPBRec): OSStatus;
  141.         (* This routine is a simple wrapper around PBGetCatInfo.  See IM:Files
  142.             for a description of the meaning of ioFDirIndex.
  143.             Note that, despite the name, this routine can be called under System 6.
  144.         *)
  145.         
  146.     function FSpSetCatInfo (var fss: FSSpec; var cpb: CInfoPBRec): OSStatus;
  147.         (* This routine is a simple wrapper around PBSetCatInfo.
  148.             Note that, despite the name, this routine can be called under System 6.
  149.         *)
  150.         
  151.     function FSpCatMoveQ(var fss : FSSpec; destDirID : longint) : OSStatus;
  152.         (*    A nicer wrapper around PBCatMove.  FSpCatMove is a horrible
  153.             routine because it requires a dest FSSpec, rather than
  154.             a dest dirID.  CatMove is not good either because the
  155.             Pascal interfaces define it to take a var:Str255 rather
  156.             than a StringPtr, so you can't pass nil for ioNewName.
  157.             So instead, we write our own.
  158.         *)
  159.  
  160.     function FileLocked (const fss: FSSpec): Boolean;
  161.         (* This routine returns true if the specified file is locked. Note that
  162.             this provides no guarantee that you can write to the file; it merely
  163.             checks all the things it can to see if any of them disable writing.
  164.         *)
  165.         
  166.     function CopyFork (sourceForkRefnum, destForkRefnum: integer; bytesToCopy: longint): OSStatus;
  167.         (* This routine copies a file fork from sourceForkRefnum to destForkRefnum.
  168.             The files must be positioned at where you want to start copying (usually
  169.             at the beginning) and the routine copys bytesToCopy bytes from the source
  170.             to the destination.
  171.         *)
  172.  
  173.     function CopyForkToFork (var sourceFile: FSSpec; var destFile: FSSpec;
  174.                             sourceRsrc: Boolean; destRsrc: Boolean): OSErr;
  175.         (* This routine copies a fork from the sourceFile to the destFile.
  176.             The fork chosen in each case is determined by the sourceRsrc and
  177.             destRsrc switches.  If true, the resource fork is used, if false,
  178.             the data fork.
  179.         *)
  180.  
  181.     function CopyFile (const source : FSSpec; const dest: FSSpec): OSStatus;
  182.         (* Copies a file from source to dest.  Any file already existing at dest is
  183.             destroyed.  Also set the catalogue info for the dest file from the source file.
  184.         *)
  185.     
  186.     function GetFSSpecGivenFileRefNum(fileRefNum : integer; var fss : FSSpec) : OSStatus;
  187.         (* Consults the FCB of the open file to find out its FSSpec. *)
  188.  
  189.     function FileRefNumIsWriteable(fileRefNum : integer; var writeable : Boolean) : OSStatus;
  190.         (* Returns true if the FCB says that the file is writable. *)
  191.  
  192.     function EqualFSSpec(const fss1 : FSSpec; const fss2 : FSSpec) : Boolean;
  193.         (* Returns true if fss1 and fss2 denote the same file system object. *)
  194.     
  195.     function ICUFSSpecToFullPath (const fss: FSSpec; var path: Str255): OSErr;
  196.         (* Returns a full path for the given FSSpec.  Actually it's an approximation
  197.             (it doesn't handle paths longer than 256 characters) but, seeing as
  198.             IC only uses this path for display purposes, that's not a problem.
  199.         *)
  200.  
  201.     function ICFileSpecToFSSpec (fileSpec: ICFileSpecHandle; canInteract: Boolean; var fss: FSSpec): ICError;
  202.     function FSSpecToICFileSpec (var fss: FSSpec; fileSpec: ICFileSpecHandle): OSErr;
  203.         (* These routines convert ICFileSpecHandles to FSSpecs and vice versa.
  204.             An ICFileSpecHandle is basically an AliasHandle with some fields tacked
  205.             on the front to make it usable by System 6 clients.  Under System 7 these
  206.             routines are basically wrappers for standard Alias Manager routines.
  207.             Note that in poth of these routines, the caller is responsible for allocating
  208.             and deallocating the ICFileSpecHandle, and we just resize it if necessary.
  209.         *)
  210.  
  211.     function IsApplicationType(fdType : OSType) : Boolean;
  212.         (*    Returns true if fdType is common type for applications. *)
  213.  
  214.     (* ***** IC API Stuff ***** *)
  215.  
  216.     (* These are simple wrappers around the IC API for getting and setting PString preferences. *)
  217.  
  218.     function ICGetPrefStr (inst: ICInstance; key: Str255; var attr: ICAttr; var str: Str255): ICError;
  219.     function ICSetPrefStr (inst: ICInstance; key: Str255; attr: ICAttr; str: Str255): ICError;
  220.  
  221.     (* ***** Text Utilities Stuff ***** *)
  222.  
  223.     (* These are simple wrappers around the toolbox NumToString and StringToNum routines. *)
  224.  
  225.     function DecStr(aNumber : longint): Str255;
  226.     function DecVal(aString : Str255) : longint;
  227.  
  228.     (* These are simple routines to convert between strings and OSTypes. *)
  229.     
  230.     function StringToOSType (aString: Str255): OSType;
  231.     function OSTypeToString (anOSType: OSType): Str15;
  232.  
  233.     function TPCopy (sourceString: string; startIndex : integer; count: integer): string;
  234.         (* TPCopy provides a version of the Pascal built-in function Copy that
  235.             implements the Think Pascal semantics.  This is much more useful
  236.             than routine built-in to Metrowerks Pascal, which implements the
  237.             semantics of MPW Pascal.
  238.             The routine extracts count characters from the source string
  239.             starting at character position start.  If there aren't enough characters
  240.             in the string, it returns what there are.
  241.         *)
  242.         
  243.     function GetOwnerName: Str255;
  244.         (* This function returns the Owner name for the Macintosh
  245.             as defined in the Sharing Setup control panel.
  246.         *)
  247.         
  248.     function NewLookupError(NersID : integer; errNum : OSStatus) : Str255;
  249.     (* Return the string associated with errNum.  The 'Ners' resource
  250.         has a ResEdit template of...
  251.                 *****
  252.                 LSTB
  253.                     errNum
  254.                     DLNG
  255.                     errstr
  256.                     PSTR
  257.                 *****
  258.                 LSTE
  259.         There must be a terminating entry with error number of 0 that contains
  260.         the default error message.
  261.     *)
  262.  
  263.     procedure NewLookupErrorC(NersID : integer; errNum : OSStatus; var result : Str255);
  264.     (* A procedure version of the above, for access from lame C-like
  265.         languages.
  266.     *)
  267.  
  268.     (* ***** Truly Misc Stuff ***** *)
  269.  
  270.     function TrapAvailable (theTrap: integer): Boolean;
  271.         (* Returns true if theTrap is available on this machine.
  272.             This routine is implemented by the book, the book being Inside Macintosh.
  273.         *)
  274.  
  275.     {$ifc not GENERATINGCFM}
  276.  
  277.     procedure MakeDataExecutableAs68KCode(base : Ptr; size : longint);
  278.         (* Makes some data executable as 68K code.  This is distinct
  279.             from the system call MakeDataExecutable, which makes data
  280.             executable as PowerPC code.
  281.            This call makes sense even on PPC machines, for example
  282.               the old contents of the memory might be currently cached
  283.               in the DR (Dynamic Recompiling) translation cache.
  284.         *)
  285.     
  286.     {$endc}
  287.  
  288.     function ICUCanInteract: ICError;
  289.         (* Returns noErr if user interaction is possible.  Basically a wrapper
  290.             around AEInteractWithUser that makes it System 6 safe.
  291.         *)
  292.  
  293.     procedure SafeAppendMenu (menuH: MenuHandle; itemText: Str255);
  294.         (* The system AppendMenu interprets the new menu item in strange
  295.             ways, attempting to glean command key and other information from 
  296.             the text.  This is obviously bad for things like the items on the 
  297.             Archie menu.  This 'safe' version of the AppendMenu routine
  298.             sets the text window interpreting it.
  299.         *)
  300.  
  301. implementation
  302.  
  303.     uses
  304.         Icons, 
  305.         Errors, 
  306.         Resources, 
  307.         Dialogs, 
  308.         ToolUtils, 
  309.         Traps, 
  310.         LowMem,
  311.         GestaltEqu,
  312.         FSM,
  313.  
  314.         InternetConfig,
  315.         ICDebug;
  316.  
  317.     (* ***** Memory Stuff ***** *)
  318.  
  319. {$PUSH}
  320. {$R-}
  321.     function BlockCompare (lhsBaseAddr : univ Ptr; rhsBaseAddr: univ Ptr; size: longint): Boolean;
  322.         (* See comment in interface part. *)
  323.     begin
  324.         BlockCompare := false;
  325.         while (size > 0) do begin
  326.             if lhsBaseAddr^ <> rhsBaseAddr^ then begin
  327.                 exit(BlockCompare);
  328.             end; (* if *)
  329.             inc(longint(lhsBaseAddr));
  330.             inc(longint(rhsBaseAddr));
  331.             size := size - 1;
  332.         end; (* while *)
  333.         BlockCompare := true;
  334.     end; (* BlockCompare *)
  335.  
  336.     procedure BlockFill (baseAddr: univ Ptr; size: longint; value: integer);
  337.         (* See comment in interface part. *)
  338.     begin
  339.         while (size > 0) do begin
  340.             baseAddr^ := value;
  341.             inc(longint(baseAddr));
  342.             size := size - 1;
  343.         end; (* while *)
  344.     end; (* BlockFill *)
  345. {$POP}
  346.  
  347.     (* ***** Resource Manager Stuff ***** *)
  348.  
  349.     function CheckMemError(memoryHandle : univ Handle) : ICError;
  350.         var
  351.             err : ICError;
  352.     begin
  353.         err := MemError;
  354.         if err = noErr then begin
  355.             ICAssert(memoryHandle <> nil);
  356.             if memoryHandle = nil then begin
  357.                 err := memFullErr;
  358.             end; (* if *)
  359.         end; (* if *)
  360.         CheckMemError := err;
  361.     end; (* CheckMemError *)
  362.     
  363.     function CheckResError(resourceHandle : univ Handle) : ICError;
  364.         var
  365.             err : ICError;
  366.     begin
  367.         err := ResError;
  368.         if err = noErr then begin
  369.             if resourceHandle = nil then begin
  370.                 err := resNotFound;
  371.             end; (* if *)
  372.         end; (* if *)
  373.         CheckResError := err;
  374.     end; (* CheckResError *)
  375.  
  376.     function AddNamedResource(data : Handle; theType : ResType; const name : Str255) : ICError;
  377.         var
  378.             err : ICError;
  379.             id : integer;
  380.     begin
  381.         repeat
  382.             id := Unique1ID(theType);
  383.         until id > 127;
  384.         AddResource(data, theType, id, name);
  385.         err := ResError;
  386.         AddNamedResource := err;
  387.     end; (* AddNamedResource *)
  388.  
  389.     function Set1Resource(theData : Handle; theType : ResType; theID : integer) : OSStatus;
  390.         (*    This routine sets the resource denoted by theType and theID to contain
  391.             theData.  If the resource does not currently exist, it is created.
  392.             If it does currently exist, it is modified.  theData is not disposed
  393.             of.
  394.         *)
  395.         var
  396.             err : OSStatus;
  397.             theDataSize : longint;
  398.             oldData : Handle;
  399.     begin
  400.         theDataSize := GetHandleSize(theData);
  401.         
  402.         // Get the current contents of the resource.
  403.         
  404.         oldData := Get1Resource(theType, theID);
  405.         err := CheckResError(oldData);
  406.         if err = resNotFound then begin
  407.         
  408.             // There is currently no resource, add one that meets our needs.
  409.             
  410.             oldData := NewHandle(theDataSize);
  411.             err := CheckMemError(oldData);
  412.             if err = noErr then begin
  413.                 BlockMoveData(theData^, oldData^, theDataSize);
  414.                 AddResource(oldData, theType, theID, '');
  415.                 err := ResError;
  416.                 if err <> noErr then begin
  417.                     
  418.                     // If AddResource failed, oldData is still a memory
  419.                     // handle (ie it won't be cleaned up by the Resource Manager
  420.                     // when the resource file is closed), so we need to clean it
  421.                     // up ourselves.
  422.                     
  423.                     DisposeHandle(oldData);
  424.                     ICAssert(MemError = noErr);
  425.                 end; (* if *)
  426.             end; (* if *)
  427.             
  428.         end else begin
  429.         
  430.             // There is current a resource, copy our data into it.
  431.             
  432.             SetHandleSize(oldData, theDataSize);
  433.             err := MemError;
  434.             if err = noErr then begin
  435.                 BlockMoveData(theData^, oldData^, theDataSize);
  436.                 ChangedResource(oldData);
  437.                 err := ResError;
  438.             end; (* if *)
  439.             
  440.         end; (* if *)
  441.         Set1Resource := err;
  442.     end; (* Set1Resource *)
  443.     
  444.     function Set1ResourcePtr(theData : univ Ptr; theDataSize : longint; theType : ResType; theID : integer) : OSStatus;
  445.         var
  446.             err : OSStatus;
  447.             tmpH : Handle;
  448.     begin
  449.         err := PtrToHand(theData, tmpH, theDataSize);
  450.         if err = noErr then begin
  451.             err := Set1Resource(tmpH, theType, theID);
  452.             DisposeHandle(tmpH);
  453.             ICAssert(MemError = noErr);
  454.         end; (* if *)
  455.         Set1ResourcePtr := err;
  456.     end; (* Set1ResourcePtr *)
  457.     
  458.     function Set1NamedResource(theData : Handle; theType : ResType; const name : Str255) : OSStatus;
  459.         (*    This routine sets the resource denoted by theType and name to contain
  460.             theData.  If the resource does not currently exist, it is created with
  461.             a unique ID greater than 127.  If it does currently exist, it is modified.
  462.             theData is not disposed of.
  463.         *)
  464.         var
  465.             err : OSStatus;
  466.             theDataSize : longint;
  467.             oldData : Handle;
  468.     begin
  469.         theDataSize := GetHandleSize(theData);
  470.         
  471.         // Get the current contents of the resource.
  472.         
  473.         oldData := Get1NamedResource(theType, name);
  474.         err := CheckResError(oldData);
  475.         if err = resNotFound then begin
  476.         
  477.             // There is currently no resource, add one that meets our needs.
  478.             
  479.             oldData := NewHandle(theDataSize);
  480.             err := CheckMemError(oldData);
  481.             if err = noErr then begin
  482.                 BlockMoveData(theData^, oldData^, theDataSize);
  483.                 err := AddNamedResource(oldData, theType, name);
  484.                 if err <> noErr then begin
  485.                     
  486.                     // If AddNamedResource failed, oldData is still a memory
  487.                     // handle (ie it won't be cleaned up by the Resource Manager
  488.                     // when the resource file is closed), so we need to clean it
  489.                     // up ourselves.
  490.                     
  491.                     DisposeHandle(oldData);
  492.                     ICAssert(MemError = noErr);
  493.                 end; (* if *)
  494.             end; (* if *)
  495.             
  496.         end else begin
  497.         
  498.             // There is current a resource, copy our data into it.
  499.             
  500.             SetHandleSize(oldData, theDataSize);
  501.             err := MemError;
  502.             if err = noErr then begin
  503.                 BlockMoveData(theData^, oldData^, theDataSize);
  504.                 ChangedResource(oldData);
  505.                 err := ResError;
  506.             end; (* if *)
  507.             
  508.         end; (* if *)
  509.         Set1NamedResource := err;
  510.     end; (* Set1NamedResource *)
  511.     
  512.     (* ***** File Manager Stuff ***** *)
  513.  
  514.     function GetVolInfo (var ioName: Str63; var ioVRefNum: integer; ioVolIndex: integer;
  515.                                         var ioVCrDate: longint): OSStatus;
  516.         (* See comment in interface part. *)
  517.         var
  518.             err: OSStatus;
  519.             pb: ParamBlockRec;
  520.     begin
  521.         (* If we're trying to look up a volume by name, make sure there's a colon
  522.             on the end of the name.
  523.         *)
  524.         if (ioName <> '') & (ioName[length(ioName)] <> ':') then begin
  525.             ioName := concat(ioName, ':');
  526.         end; (* if *)
  527.         pb.ioNamePtr := @ioName;
  528.         pb.ioVRefNum := ioVRefNum;
  529.         pb.ioVolIndex := ioVolIndex;
  530.         err := PBGetVInfoSync(@pb);
  531.         if err = noErr then begin
  532.             ioVRefNum := pb.ioVRefNum;
  533.             ioVCrDate := pb.ioVCrDate;
  534.         end; (* if *)
  535.         GetVolInfo := err;
  536.     end; (* GetVolInfo *)
  537.  
  538.     function FindApplicationInDTDB (creator: OSType; var foundApplicationSpec: FSSpec): OSStatus;
  539.         (* See comment in interface part. *)
  540.         var
  541.             err: OSStatus;
  542.             junkCreationDate: longint;
  543.             volumeIndex: integer;
  544.             pbdt: DTPBRec;
  545.             found: Boolean;
  546.     begin
  547.         found := false;
  548.         (* Repeat through each of the volumes in their enumeration order,
  549.             querying the DTDB on each volume.
  550.         *)
  551.         volumeIndex := 1;
  552.         repeat
  553.             foundApplicationSpec.vRefNum := 0;
  554.             foundApplicationSpec.name := '';
  555.             err := GetVolInfo(foundApplicationSpec.name, foundApplicationSpec.vRefNum, volumeIndex, junkCreationDate);
  556.  
  557.             (* On this volume, attempt to find the application.  First get the path
  558.                 for the DTDB.
  559.             *)
  560.             if err = noErr then begin
  561.                 foundApplicationSpec.name := '';
  562.                 pbdt.ioNamePtr := @foundApplicationSpec.name;
  563.                 pbdt.ioVRefNum := foundApplicationSpec.vRefNum;
  564.                 err := PBDTGetPath(@pbdt);
  565.                 
  566.                 (* We have the path for the DTDB, now lookup the application in it. *)
  567.                 if err = noErr then begin
  568.                     pbdt.ioIndex := 0;
  569.                     pbdt.ioFileCreator := creator;
  570.                     err := PBDTGetAPPLSync(@pbdt);
  571.                     if err = noErr then begin
  572.                         found := true;
  573.                     end; (* if *)
  574.                 end; (* if *)
  575.                 
  576.                 (* Ignore errors from the DTDB, so we continue on with the next volume. *)
  577.                 err := noErr;
  578.             end; (* if *)
  579.             volumeIndex := volumeIndex + 1;
  580.         until found or (err <> noErr);
  581.         
  582.         (* Clean up.  If we found the application, set the parID.  Otherwise return an
  583.             innocuous FSSpec.
  584.         *)
  585.         if found then begin
  586.             err := noErr;
  587.             foundApplicationSpec.parID := pbdt.ioAPPLParID;
  588.         end else begin
  589.             err := afpItemNotFound;
  590.             foundApplicationSpec.vRefNum := 0;
  591.             foundApplicationSpec.parID := 2;
  592.             foundApplicationSpec.name := '';
  593.         end; (* if *)
  594.         FindApplicationInDTDB := err;
  595.     end; (* FindApplicationInDTDB *)
  596.  
  597.     function FSpGetCatInfo (var fss: FSSpec; ioFDirIndex: integer; var cpb: CInfoPBRec): OSStatus;
  598.         (* See comment in interface part. *)
  599.     begin
  600.         cpb.ioVRefNum := fss.vRefNum;
  601.         cpb.ioDirID := fss.parID;
  602.         cpb.ioNamePtr := @fss.name;
  603.         cpb.ioFDirIndex := ioFDirIndex;
  604.         FSpGetCatInfo := PBGetCatInfoSync(@cpb);
  605.     end; (* FSpGetCatInfo *)
  606.  
  607.     function FSpSetCatInfo (var fss: FSSpec; var cpb: CInfoPBRec): OSStatus;
  608.         (* See comment in interface part. *)
  609.     begin
  610.         cpb.ioVRefNum := fss.vRefNum;
  611.         cpb.ioDirID := fss.parID;
  612.         cpb.ioNamePtr := @fss.name;
  613.         FSpSetCatInfo := PBSetCatInfoSync(@cpb);
  614.     end; (* FSpSetCatInfo *)
  615.  
  616.     function FSpCatMoveQ(var fss : FSSpec; destDirID : longint) : OSStatus;
  617.         (* See comment in interface part. *)
  618.         var
  619.             cmpb : CMovePBRec;
  620.     begin
  621.         cmpb.ioNamePtr := @fss.name;
  622.         cmpb.ioVRefNum := fss.vRefNum;
  623.         cmpb.ioDirID := fss.parID;
  624.         cmpb.ioNewName := nil;
  625.         cmpb.ioNewDirID := destDirID;
  626.         FSpCatMoveQ := PBCatMoveSync(@cmpb);
  627.     end; (* FSpCatMoveQ *)
  628.  
  629.     function IsVolumeWriteable (vRefNum: integer): OSStatus;
  630.         (* This routine returns noErr if the specified volume is writable,
  631.             or an appropriate error otherwise.
  632.         *)
  633.         var
  634.             err: OSStatus;
  635.             pb: HParamBlockRec;
  636.     begin
  637.         pb.ioVRefNum := vRefNum;
  638.         pb.ioNamePtr := nil;
  639.         pb.ioVolIndex := 0;
  640.         err := PBHGetVInfoSync(@pb);
  641.  
  642.         if err = noErr then begin
  643.             if band(pb.ioVAtrb, $0080) <> 0 then begin
  644.                 err := wPrErr;            (* volume locked by hardware *)
  645.             end else if band(pb.ioVAtrb, $8000) <> 0 then begin
  646.                 err := vLckdErr;            (* volume locked by software *)
  647.             end; (* if *)
  648.         end; (* if *)
  649.  
  650.         IsVolumeWriteable := err;
  651.     end; (* IsVolumeWriteable *)
  652.  
  653.     function IsFileWriteable (fss: FSSpec): OSStatus;
  654.         (* This routine returns noErr if the specified file is writeable,
  655.             or an appropriate error otherwise.
  656.         *)
  657.         var
  658.             err: OSStatus;
  659.             cpb: CInfoPBRec;
  660.     begin
  661.         err := FSpGetCatInfo(fss, 0, cpb);
  662.         if err = noErr then begin
  663.             if band(cpb.ioFlAttrib, $01) <> 0 then begin
  664.                 err := fLckdErr;
  665.             end; (* if *)
  666.         end; (* if *)
  667.         IsFileWriteable := err;
  668.     end; (* IsFileWriteable *)
  669.  
  670.     function HGetDirAccess (ioVRefNum: integer; ioDirID: longint; ioName: StringPtr;
  671.                                             var ownerID, groupID, accessRights: longint): OSStatus;
  672.         (* This routine returns the directory access privileges for the specified directory. *)
  673.         var
  674.             err: OSStatus;
  675.             pb: HParamBlockRec;
  676.     begin
  677.         pb.ioNamePtr := ioName;
  678.         pb.ioVRefNum := ioVRefNum;
  679.         pb.ioDirID := ioDirID;
  680.         err := PBHGetDirAccessSync(@pb);
  681.         ownerID := pb.ioACOwnerID;
  682.         groupID := pb.ioACGroupID;
  683.         accessRights := pb.ioACAccess;
  684.         HGetDirAccess := err;
  685.     end; (* HGetDirAccess *)
  686.  
  687.     function FileLocked (const fss: FSSpec): Boolean;
  688.         (* See comment in interface part. *)
  689.         var
  690.             locked: Boolean;
  691.             junk: longint;
  692.             access: longint;
  693.     begin
  694.         locked := (IsVolumeWriteable(fss.vRefNum) <> noErr);
  695.         if not locked then begin
  696.             locked := (IsFileWriteable(fss) <> noErr);
  697.         end; (* if *)
  698.         if not locked then begin
  699.             if HGetDirAccess(fss.vRefNum, fss.parID, nil, junk, junk, access) = noErr then begin
  700.                 locked := not btst(access, 26);
  701.             end; (* if *)
  702.         end; (* if *)
  703.         FileLocked := locked;
  704.     end; (* FileLocked *)
  705.  
  706.     function CopyFork (sourceForkRefnum, destForkRefnum: integer; bytesToCopy: longint): OSStatus;
  707.         (*    See comments in interface part. *)
  708.         const
  709.             kMaxCopyBufferSize = 65536;
  710.             kMinCopyBufferSize = 512;
  711.         var
  712.             err: OSStatus;
  713.             copyBuffer: Ptr;
  714.             copyBufferSize: longint;
  715.             numberOfBytesThisTime: longint;
  716.     begin
  717.         (* First attempt to allocate a copy buffer.  We do this by attempting
  718.             to allocate a buffer of size kMaxCopyBufferSize.  If that fails,
  719.             we divide the size by two and try again.  We keep trying until
  720.             the size drops below kMinCopyBufferSize, after which we
  721.             give up and return an error.
  722.         *)
  723.         err := noErr;
  724.         copyBufferSize := kMaxCopyBufferSize;
  725.         copyBuffer := nil;
  726.         repeat
  727.             copyBuffer := NewPtr(copyBufferSize);
  728.             if copyBuffer = nil then begin
  729.                 copyBufferSize := copyBufferSize div 2;
  730.             end; (* if *)
  731.         until (copyBuffer <> nil) or (copyBufferSize < kMinCopyBufferSize);
  732.         if copyBuffer = nil then begin
  733.             err := memFullErr;
  734.         end; (* if *)
  735.         
  736.         (* Now copy the file data, in copyBufferSize chunks. *)
  737.         while (err = noErr) & (bytesToCopy > 0) do begin
  738.             numberOfBytesThisTime := copyBufferSize;
  739.             if numberOfBytesThisTime > bytesToCopy then begin
  740.                 numberOfBytesThisTime := bytesToCopy;
  741.             end; (* if *)
  742.             err := FSRead(sourceForkRefnum, numberOfBytesThisTime, copyBuffer);
  743.             if err = noErr then begin
  744.                 bytesToCopy := bytesToCopy - numberOfBytesThisTime;
  745.                 err := FSWrite(destForkRefnum, numberOfBytesThisTime, copyBuffer);
  746.             end; (* if *)
  747.         end; (* while *)
  748.         
  749.         (* Clean up. *)
  750.         if copyBuffer <> nil then begin
  751.             DisposePtr(copyBuffer);
  752.         end; (* if *)
  753.         CopyFork := err;
  754.     end; (* CopyFork *)
  755.  
  756.     function CopyForkToFork (var sourceFile: FSSpec; var destFile: FSSpec;
  757.                             sourceRsrc: Boolean; destRsrc: Boolean): OSErr;
  758.         (*    See comments in interface part. *)
  759.         var
  760.             err: OSErr;
  761.             srcRefNum: integer;
  762.             destRefNum: integer;
  763.             sizeofSrcFork: longint;
  764.             junk: OSErr;
  765.     begin
  766.         (* Prepare for failure. *)
  767.         srcRefNum := 0;
  768.         destRefNum := 0;
  769.         
  770.         (* Open the source fork. *)
  771.         if sourceRsrc then begin
  772.             err := FSpOpenRF(sourceFile, fsRdPerm, srcRefNum);
  773.         end else begin
  774.             err := FSpOpenDF(sourceFile, fsRdPerm, srcRefNum);
  775.         end; (* if *)
  776.         if err <> noErr then begin
  777.             srcRefNum := 0;
  778.         end; (* if *)
  779.         
  780.         (* Open the dest fork. *)
  781.         if err = noErr then begin
  782.             if destRsrc then begin
  783.                 err := FSpOpenRF(destFile, fsRdWrPerm, destRefNum);
  784.             end else begin
  785.                 err := FSpOpenDF(destFile, fsRdWrPerm, destRefNum);
  786.             end; (* if *)
  787.             if err <> noErr then begin
  788.                 destRefNum := 0;
  789.             end; (* if *)
  790.         end; (* if *)
  791.  
  792.         (* Set the length of the dest fork to the length of the source fork. *)
  793.         if err = noErr then begin
  794.             err := GetEOF(srcRefNum, sizeofSrcFork);
  795.         end; (* if *)
  796.         if err = noErr then begin
  797.             err := SetEOF(destRefNum, sizeofSrcFork);
  798.         end; (* if *)
  799.  
  800.         (* Copy the fork. *)
  801.         if err = noErr then begin
  802.             err := CopyFork(srcRefNum, destRefNum, sizeofSrcFork);
  803.         end; (* if *)
  804.  
  805.         (* Clean up. *)
  806.         if srcRefNum <> 0 then begin
  807.             junk := FSClose(srcRefNum);
  808.         end; (* if *)
  809.         if destRefNum <> 0 then begin
  810.             junk := FSClose(destRefNum);
  811.         end; (* if *)
  812.  
  813.         junk := FlushVol(nil, destFile.vRefNum);
  814.  
  815.         CopyForkToFork := err;
  816.     end; (* CopyForkToFork *)
  817.     
  818.     function CopyFile (const source : FSSpec; const dest: FSSpec): OSStatus;
  819.         (* See comment in interface part. *)
  820.         var
  821.             err : OSStatus;
  822.             junk: OSStatus;
  823.             cpb: CInfoPBRec;
  824.             tmpSource : FSSpec;
  825.             tmpDest : FSSpec;
  826.     begin
  827.         tmpSource := source;
  828.         tmpDest := dest;
  829.         
  830.         (* Start off by deleting the destination file. *)
  831.         junk := FSpDelete(tmpDest);
  832.         
  833.         (* Copy both forks of the file. *)
  834.         err := FSpGetCatInfo(tmpSource, 0, cpb);
  835.         if err = noErr then begin
  836.             err := FSpCreate(tmpDest, cpb.ioFlFndrInfo.fdCreator, cpb.ioFlFndrInfo.fdType, smSystemScript);
  837.         end; (* if *)
  838.         if err = noErr then begin
  839.             err := CopyForkToFork (tmpSource, tmpDest, false, false);
  840.         end; (* if *)
  841.         if err = noErr then begin
  842.             err := CopyForkToFork (tmpSource, tmpDest, true, true);
  843.         end; (* if *)
  844.         
  845.         (* Set the catalogue info for the tmpDestination file. *)
  846.         if err = noErr then begin
  847.             err := FSpSetCatInfo(tmpDest, cpb);
  848.         end; (* if *)
  849.         
  850.         (* Clean up.  Delete the file if we didn't succeed completely. *)
  851.         if err <> noErr then begin
  852.             junk := FSpDelete(tmpDest);
  853.         end; (* if *)
  854.         CopyFile := err;
  855.     end; (* CopyFile *)
  856.  
  857.     function GetFSSpecGivenFileRefNum(fileRefNum : integer; var fss : FSSpec) : OSStatus;
  858.         (* Consults the FCB of the open file to find out its FSSpec. *)
  859.         var
  860.             err : OSStatus;
  861.             fcbPB : FCBPBRec;
  862.     begin
  863.         fcbPB.ioNamePtr := @fss.name;
  864.         fcbPB.ioRefNum := fileRefNum;
  865.         fcbPB.ioFCBIndx := 0;
  866.         fcbPB.ioVRefNum := 0;
  867.         err := PBGetFCBInfoSync(@fcbPB);
  868.         if err = noErr then begin
  869.             fss.vRefNum := fcbPB.ioFCBVRefNum;
  870.             fss.parID := fcbPB.ioFCBParID;
  871.         end; (* if *)
  872.         GetFSSpecGivenFileRefNum := err;
  873.     end; (* GetFSSpecGivenFileRefNum *)
  874.  
  875.     function FileRefNumIsWriteable(fileRefNum : integer; var writeable : Boolean) : OSStatus;
  876.         (* See comment in interface part. *)
  877.         var
  878.             err : OSStatus;
  879.             fcbPB : FCBPBRec;
  880.     begin
  881.         fcbPB.ioNamePtr := nil;
  882.         fcbPB.ioRefNum := fileRefNum;
  883.         fcbPB.ioFCBIndx := 0;
  884.         fcbPB.ioVRefNum := 0;
  885.         err := PBGetFCBInfoSync(@fcbPB);
  886.         if err = noErr then begin
  887.             writeable := band(fcbPB.ioFCBFlags, bsl(fcbWriteMask, 8)) <> 0;
  888.         end; (* if *)
  889.         FileRefNumIsWriteable := err;
  890.     end; (* FileRefNumIsWriteable *)
  891.  
  892.     function EqualFSSpec(const fss1 : FSSpec; const fss2 : FSSpec) : Boolean;
  893.         (* Returns true if fss1 and fss2 denote the same file system object. *)
  894.     begin
  895.         EqualFSSpec := (fss1.vRefNum = fss2.vRefNum) &
  896.                         (fss1.parID = fss2.parID) &
  897.                         EqualString(fss1.name, fss2.name, false, true);
  898.     end; (* EqualFSSpec *)
  899.  
  900.     function ICUFSSpecToFullPath (const fss: FSSpec; var path: Str255): OSErr;
  901.         (* See comment in interface part. *)
  902.         var
  903.             err: OSErr;
  904.             pb: CInfoPBRec;
  905.             tmpFSS : FSSpec;
  906.     begin
  907.         tmpFSS := fss;
  908.         
  909.         err := noErr;
  910.         if tmpFSS.parID = 1 then begin
  911.             (* It's a volume, just return the “name:”. *)
  912.             path := concat(tmpFSS.name, ':');
  913.         end else begin
  914.         
  915.             (* It's a file or folder, start by putting the name at the end of the path
  916.                 and then iterate up the directory hierarchy adding directory names to
  917.                 the front of the path.
  918.             *)
  919.             path := tmpFSS.name;
  920.             while (err = noErr) & (tmpFSS.parID <> 1) do begin
  921.                 err := FSpGetCatInfo(tmpFSS, -1, pb);
  922.                 path := concat(tmpFSS.name, ':', path);
  923.                 tmpFSS.parID := pb.ioFlParID;
  924.             end; (* while *)
  925.             
  926.         end; (* if *)
  927.         ICUFSSPecToFullPath := err;
  928.     end; (* ICUFSSPecToFullPath *)
  929.  
  930.     function FindVolumeByNameAndDate (name: Str31; creationDate: longint; var vRefNum: integer): OSErr;
  931.         (* Attempts to find a volume based on it's name and creation date.  This
  932.             is the tricky part of our "poor man's alias resolution" scheme.  An
  933.             ICFileSpec stores the volume name and creation date of the item
  934.             it points to.  These are used to try to find the matching volume
  935.             in systems that don't have the Alias Manager.  This routine implements
  936.             that finding code.
  937.             
  938.             The routine takes a two phase approach.  In the first phase, it searches for
  939.             volumes by name and creation date.  If it can't find a match, it proceeds
  940.             to the second phase where a matching name is considered good enough.
  941.             
  942.             Please don't blame me for the number of "leave"s in this code.  Peter
  943.             wrote it, and I've learnt through hard experience that I'm too stupid
  944.             to mess with his code too much.
  945.         *)
  946.         var
  947.             err: OSErr;
  948.             phase : (kMatchNameAndCreationDate, kMatchOnlyName);
  949.             volumeName: Str255;
  950.             volumeIndex: integer;
  951.             pb: HParamBlockRec;
  952.     begin
  953.         for phase := kMatchNameAndCreationDate to kMatchOnlyName do begin
  954.  
  955.             volumeIndex := 1;
  956.             while true do begin
  957.                 (* Get info an the volumeIndex'th volume. *)
  958.                 volumeName := '';
  959.                 pb.ioNamePtr := @volumeName;
  960.                 pb.ioVolIndex := volumeIndex;
  961.                 err := PBGetVInfoSync(@pb);
  962.                 if err <> noErr then begin
  963.                     leave;
  964.                 end; (* if *)
  965.  
  966.                 (* Check for a match. *)
  967.                 if EqualString(name, volumeName, false, true) then begin
  968.                     if (phase = kMatchOnlyName) or (pb.ioVCrDate = creationDate) then begin
  969.                         leave;
  970.                     end; (* if *)
  971.                 end; (* if *)
  972.  
  973.                 volumeIndex := volumeIndex + 1;
  974.             end; (* while *)
  975.  
  976.             (* Leave if we found a match. *)
  977.             if err = noErr then begin
  978.                 leave;
  979.             end; (* if *)
  980.         end; (* for *)
  981.         
  982.         (* Return the vRefNum of the found volume. *)
  983.         if err = noErr then begin
  984.             vRefNum := pb.ioVRefNum;
  985.         end; (* if *)
  986.         FindVolumeByNameAndDate := err;
  987.     end; (* FindVolumeByNameAndDate *)
  988.  
  989.     function ICFileSpecToFSSpec (fileSpec: ICFileSpecHandle; canInteract: Boolean; var fss: FSSpec): ICError;
  990.         (* See comment in interface part. *)
  991.         var
  992.             err: ICError;
  993.             junkLong: longint;
  994.             aliasH: AliasHandle;
  995.             aliasCount: integer;
  996.             aliasMatchRules: longint;
  997.             junkBool: Boolean;
  998.             cpb: CInfoPBRec;
  999.     begin
  1000.         err := noErr;
  1001.         if (err = noErr) & (GetHandleSize(Handle(fileSpec)) < sizeof(ICFileSpec)) then begin
  1002.             err := paramErr;
  1003.         end; (* if *)
  1004.         
  1005.         if err = noErr then begin
  1006.  
  1007.             (* Try to find it using the alias embedded in the ICFileSpec. *)
  1008.             err := -1;
  1009.             if (fileSpec^^.alias.aliasSize <> 0) then begin
  1010.                 (* Make a copy of the ICFileSpecHandle. *)
  1011.                 aliasH := AliasHandle(fileSpec);
  1012.                 err := HandToHand(Handle(aliasH));
  1013.                 if err = noErr then begin
  1014.                     (* Use Munger to delete our fields from the front of the copy, thereby
  1015.                         turning it into a real AliasHandle.
  1016.                     *)
  1017.                     junkLong := Munger(Handle(aliasH), 0, nil, sizeof(ICFileSpec) - sizeof(AliasRecord), @junkLong, 0);
  1018.  
  1019.                     (* Call the Alias Manager to find the match. *)
  1020.                     aliasCount := 1;
  1021.                     aliasMatchRules := kARMSearch + kARMMountVol;
  1022.                     if canInteract & (ICUCanInteract <> noErr) then begin
  1023.                         aliasMatchRules := aliasMatchRules + kARMNoUI;
  1024.                     end; (* if *)
  1025.                     err := MatchAlias(nil, aliasMatchRules, aliasH, aliasCount, @fss, junkBool, nil, nil);
  1026.  
  1027.                     (* Dispose our copy of the alias. *)
  1028.                     DisposeHandle(Handle(aliasH));
  1029.                 end; (* if *)
  1030.             end; (* if *)
  1031.  
  1032.             (* If it we didn't find it, try using our poor man's alias. *)
  1033.             if err <> noErr then begin
  1034.  
  1035.                 (* Attempt to find a matching volume. *)
  1036.                 err := FindVolumeByNameAndDate(fileSpec^^.vol_name, fileSpec^^.vol_creation_date, fss.vRefNum);
  1037.  
  1038.                 (* If it worked, build an FSSpec for the item and confirm it's existance using
  1039.                     GetCatInfo.
  1040.                 *)
  1041.                 if err = noErr then begin
  1042.                     fss.parID := fileSpec^^.fss.parID;
  1043.                     fss.name := fileSpec^^.fss.name;
  1044.                     err := FSpGetCatInfo(fss, 0, cpb);
  1045.                 end; (* if *)
  1046.             end; (* if *)
  1047.         end; (* if *)
  1048.  
  1049.         ICFileSpecToFSSpec := err;
  1050.     end; (* ICFileSpecToFSSpec *)
  1051.  
  1052.     function FSSpecToICFileSpec (var fss: FSSpec; fileSpec: ICFileSpecHandle): OSErr;
  1053.         (* See comment in interface part. *)
  1054.         var
  1055.             err: OSErr;
  1056.             pb: HParamBlockRec;
  1057.             volumeName: Str63;
  1058.             aliasH: AliasHandle;
  1059.             junkLong: longint;
  1060.     begin
  1061.         (* First resize the handle to the basic size and fill in our poor man's alias
  1062.             information.
  1063.         *)
  1064.         SetHandleSize(Handle(fileSpec), sizeof(ICFileSpec));
  1065.         err := MemError;
  1066.         if err = noErr then begin
  1067.             
  1068.             (* Get the volume information. *)
  1069.             volumeName := '';
  1070.             pb.ioNamePtr := @volumeName;
  1071.             pb.ioVRefNum := fss.vRefNum;
  1072.             pb.ioVolIndex := 0;
  1073.             err := PBGetVInfoSync(@pb);
  1074.             
  1075.             (* Fill in the basic fields of the ICFileSpec. *)
  1076.             if err = noErr then begin
  1077.                 fileSpec^^.vol_creation_date := pb.ioVCrDate;
  1078.                 fileSpec^^.vol_name := volumeName;
  1079.                 fileSpec^^.fss := fss;
  1080.                 fileSpec^^.alias.userType := OSType(0);
  1081.                 fileSpec^^.alias.aliasSize := 0;
  1082.             end; (* if *)
  1083.         end; (* if *)
  1084.  
  1085.         (* Now, if we have the Alias Manager, create an alias and append it to the handle. 
  1086.             This is entirely optional, so we make sure that any errors encountered in this
  1087.             process don't make it out to the client.
  1088.         *)
  1089.         if (err = noErr) then begin
  1090.         
  1091.             (* Create the alias. *)
  1092.             err := NewAlias(nil, fss, aliasH);
  1093.             if err = noErr then begin
  1094.                 
  1095.                 (* Append it to the end of the fileSpec and then delete the dummy
  1096.                     AliasRecord from the end of the original fileSpec.
  1097.                 *)
  1098.                 err := HandAndHand(Handle(aliasH), Handle(fileSpec));
  1099.                 if err = noErr then begin
  1100.                     junkLong := Munger(Handle(fileSpec), sizeof(ICFileSpec) - sizeof(AliasRecord), nil, sizeof(AliasRecord), @junkLong, 0);
  1101.                 end; (* if *)
  1102.                 DisposeHandle(Handle(aliasH));
  1103.             end; (* if *)
  1104.             err := noErr;
  1105.         end; (* if *)
  1106.  
  1107.         FSSpecToICFileSpec := err;
  1108.     end; (* FSSpecToICFileSpec *)
  1109.  
  1110.     function IsApplicationType(fdType : OSType) : Boolean;
  1111.         (* See comment in interface part. *)
  1112.     begin
  1113.         // If you add extra types, you should also make a similar change
  1114.         // in ICStandardGetFile in "ICStandardFile.p".
  1115.         
  1116.         IsApplicationType := (fdType = 'APPL') | (fdType = 'APPC') | (fdType = 'appe');
  1117.     end; (* IsApplicationType *)
  1118.  
  1119.     (* ***** IC API Stuff ***** *)
  1120.  
  1121. {$PUSH}
  1122. {$R-}
  1123.     function ICGetPrefStr (inst: ICInstance; key: Str255; var attr: ICAttr; var str: Str255): ICError;
  1124.         (* See comment in interface part. *)
  1125.         var
  1126.             err: ICError;
  1127.             size: longint;
  1128.     begin
  1129.         size := 256;
  1130.         err := ICGetPref(inst, key, attr, @str, size);
  1131.         if err <> noErr then begin
  1132.             str := '';
  1133.         end; (* if *)
  1134.         ICGetPrefStr := err;
  1135.     end; (* ICGetPrefStr *)
  1136.  
  1137.     function ICSetPrefStr (inst: ICInstance; key: Str255; attr: ICAttr; str: Str255): ICError;
  1138.         (* See comment in interface part. *)
  1139.     begin
  1140.         ICSetPrefStr := ICSetPref(inst, key, attr, @str, length(str) + 1);
  1141.     end; (* ICSetPrefStr *)
  1142. {$POP}
  1143.  
  1144.     (* ***** Text Utilities Stuff ***** *)
  1145.  
  1146.     function DecStr(aNumber: longint): Str255;
  1147.         (* See comment in interface part. *)
  1148.         var
  1149.             result : Str255;
  1150.     begin
  1151.         NumToString(aNumber, result);
  1152.         DecStr := result;
  1153.     end; (* DecStr *)
  1154.  
  1155.     function DecVal(aString : Str255) : longint;
  1156.         (* See comment in interface part. *)
  1157.         var
  1158.             result : longint;
  1159.     begin
  1160.         StringToNum(aString, result);
  1161.         DecVal := result;
  1162.     end; (* DecVal *)
  1163.  
  1164.     function StringToOSType (aString: Str255): OSType;
  1165.         (* See comment in interface part. *)
  1166.         var
  1167.             result: OSType;
  1168.     begin
  1169.         aString := concat(aString, chr(0), chr(0), chr(0), chr(0));
  1170.         BlockMoveData(@aString[1], @result, 4);
  1171.         StringToOSType := result;
  1172.     end; (* StringToOSType *)
  1173.  
  1174.     function OSTypeToString (anOSType: OSType): Str15;
  1175.         (* See comment in interface part. *)
  1176.         var
  1177.             result : Str15;
  1178.     begin
  1179.         result := concat(chr(0),chr(0),chr(0),chr(0));
  1180.         BlockMoveData(@anOSType, @result[1], 4);
  1181.         OSTypeToString := result;
  1182.     end; (* OSTypeToString *)
  1183.  
  1184.     function TPCopy (sourceString: string; startIndex : integer; count: integer): string;
  1185.         (* See comment in interface part. *)
  1186.     begin
  1187.         (* Check for startIndex being before the first character in the string. *)
  1188.         if startIndex < 1 then begin
  1189.             count := count - (1 - startIndex);
  1190.             startIndex := 1;
  1191.         end; (* if *)
  1192.         
  1193.         (* Check for a request for more characters than are in the string. *)
  1194.         if (startIndex + count) > length(sourceString) then begin
  1195.             count := length(sourceString) - startIndex + 1;
  1196.         end; (* if *)
  1197.         
  1198.         (* Trim count. *)
  1199.         if count < 0 then begin
  1200.             count := 0;
  1201.         end; (* if *)
  1202.         
  1203.         (* Extract the string data. *)
  1204.         sourceString[0] := chr(count);
  1205.         BlockMoveData(@sourceString[startIndex], @sourceString[1], count);
  1206.  
  1207.         TPCopy := sourceString;
  1208.     end; (* TPCopy *)
  1209.  
  1210.     function GetOwnerName : Str255;
  1211.         (* See comment in interface part. *)
  1212.         const
  1213.             rOwnerNameString = -16096;
  1214.         var
  1215.             strH: StringHandle;
  1216.     begin
  1217.         strH := GetString(rOwnerNameString);
  1218.         if strH <> nil then begin
  1219.             (* Don't release it, someone else might be using it. *)
  1220.             GetOwnerName := strH^^;
  1221.         end else begin
  1222.             GetOwnerName := '';
  1223.         end; (* if *)
  1224.     end; (* GetOwnerName *)
  1225.  
  1226.     function NewLookupError(NersID : integer; errNum : OSStatus) : Str255;
  1227.         (* See comment in interface part. *)
  1228.         var
  1229.             result : Str255;
  1230.             errH : Handle;
  1231.             s : SInt8;
  1232.             candidateErrNum : longint;
  1233.             errsDataPtr : BigBufferPtr;
  1234.             indexIntoErrsData : longint;
  1235.             maxIndexIntoErrsData : longint;
  1236.             found : Boolean;
  1237.     begin
  1238.         result := '';
  1239.         errH := GetResource('Ners', NersID);
  1240.         if errH <> nil then begin
  1241.             s := HGetState(errH);
  1242.             HLock(errH);
  1243.             errsDataPtr := BigBufferPtr(errH^);
  1244.             
  1245.             indexIntoErrsData := 0;
  1246.             maxIndexIntoErrsData := GetHandleSize(errH);
  1247.             found := false;
  1248.             (* Loop through the resource looking for a match. *)
  1249.             while (indexIntoErrsData < maxIndexIntoErrsData) and not found do begin
  1250.                 (* Extract the error number.
  1251.                      I use BlockMoveData here because the data may not be word aligned, and
  1252.                         original 68Ks will take an Address Error if I attempt to move an
  1253.                         unaligned longint.
  1254.                 *)
  1255.                 BlockMoveData(@errsDataPtr^[indexIntoErrsData], @candidateErrNum, sizeof(longint));
  1256.                 indexIntoErrsData := indexIntoErrsData + sizeof(longint);
  1257.  
  1258.                 (* Extract the error string. *)
  1259.                 BlockMoveData(@errsDataPtr^[indexIntoErrsData], @result, errsDataPtr^[indexIntoErrsData] + 1);
  1260.                 indexIntoErrsData := indexIntoErrsData + errsDataPtr^[indexIntoErrsData] + 1;
  1261.  
  1262.                 (* Figure out whether we've found what we're looking for. *)
  1263.                 found := (candidateErrNum = errNum) or (candidateErrNum = 0)
  1264.             end; (* while *)
  1265.             
  1266.             if not found then begin
  1267.                 result := '';
  1268.             end; (* if *)
  1269.             HSetState(errH, s);
  1270.         end; (* if *)
  1271.         NewLookupError := result;
  1272.     end; (* NewLookupError *)
  1273.  
  1274.     procedure NewLookupErrorC(NersID : integer; errNum : OSStatus; var result : Str255);
  1275.         (* See comment in interface part. *)
  1276.     begin
  1277.         result := NewLookupError(NersID, errNum);
  1278.     end; (* NewLookupErrorC *)
  1279.  
  1280.     (* ***** Truly Misc Stuff ***** *)
  1281.  
  1282.     function NumToolboxTraps: integer;
  1283.         (* Returns the number of toolbox traps on this machine. *)
  1284.     begin
  1285.         if NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) then begin
  1286.             NumToolboxTraps := $200
  1287.         end else begin
  1288.             NumToolboxTraps := $400;
  1289.         end; (* if *)
  1290.     end; (* NumToolboxTraps *)
  1291.  
  1292.     function GetTrapType (theTrap: integer): TrapType;
  1293.         (* Returns the trap type associated with the given A-Trap number. *)
  1294.         const
  1295.             TrapMask = $0800;
  1296.     begin
  1297.         if band(theTrap, TrapMask) > 0 then begin
  1298.             GetTrapType := ToolTrap
  1299.         end else begin
  1300.             GetTrapType := OSTrap;
  1301.         end; (* if *)
  1302.     end; (* GetTrapType *)
  1303.  
  1304.     function TrapAvailable (theTrap: integer): Boolean;
  1305.         (* See comment in interface part. *)
  1306.         var
  1307.             tType: TrapType;
  1308.     begin
  1309.         tType := GetTrapType(theTrap);
  1310.         if tType = ToolTrap then begin
  1311.             theTrap := band(theTrap, $07FF);
  1312.             if theTrap >= NumToolboxTraps then begin
  1313.                 theTrap := _Unimplemented;
  1314.             end; (* if *)
  1315.         end; (* if *)
  1316.         TrapAvailable := NGetTrapAddress(theTrap, tType) <> NGetTrapAddress(_Unimplemented, ToolTrap);
  1317.     end; (* TrapAvailable *)
  1318.  
  1319.     {$ifc not GENERATINGCFM}
  1320.     
  1321.     (*    Some utter gumby forgot that you might want to generate
  1322.         68K code from a PPC binary, and so FlushCodeCacheRange is
  1323.         not exported to CFM clients.  You can write your own glue,
  1324.         but in the case of IC it's just simpler to comment out the code
  1325.         for PPC builds because I don't need this functionality from
  1326.         my PPC code.
  1327.     *)
  1328.     
  1329.     (*    Some utter gumby forgot that FlushCodeCacheRange is supposed
  1330.         to return an error code, and defined it wrong in the Universal
  1331.         Interfaces.  This error is *finally* fixed in version 3.x
  1332.         of the interfaces, but we're using 2.x at the moment, so we
  1333.         still have to define our own.
  1334.  
  1335.         By the way, we define this EXTERNAL if we're building CFM,
  1336.         so that the compiler doesn't complain about the fact we
  1337.         don't implement the CFM side of it (which is kinda tricky).
  1338.         That puts off the error until link time.  If I find that
  1339.         I actually need to call this from CFM code, I guess 
  1340.         I'll have to knuckle down and write the glue.
  1341.     *)
  1342.     
  1343.     FUNCTION QFlushCodeCacheRange(address: UNIV Ptr; count: LONGINT) : OSErr;
  1344.         {$IFC NOT GENERATINGCFM}
  1345.         INLINE $225F, $205F, $7009, $A098, $3E80;
  1346.         {$ELSEC}
  1347.         EXTERNAL;
  1348.         {$ENDC}
  1349.     
  1350.     procedure MakeDataExecutableAs68KCode(base : Ptr; size : longint);
  1351.     begin
  1352.         if TrapAvailable(_HWPriv) then begin
  1353.             if QFlushCodeCacheRange(base, size) <> noErr then begin
  1354.                 FlushCodeCache;
  1355.             end; (* if *)
  1356.         end; (* if *)
  1357.     end; (* MakeDataExecutableAs68KCode *)
  1358.     
  1359.     {$endc}
  1360.  
  1361.     function ICUCanInteract: ICError;
  1362.         (* See comment in interface part. *)
  1363.         var
  1364.             err: ICError;
  1365.             gestaltResponse : longint;
  1366.     begin
  1367.         err := noErr;
  1368.         if (Gestalt(gestaltAppleEventsAttr, gestaltResponse) = noErr) &
  1369.                                     btst(gestaltResponse, gestaltAppleEventsPresent) then begin
  1370.             err := AEInteractWithUser(kAEDefaultTimeout, nil, nil);
  1371.         end; (* if *)
  1372.         ICUCanInteract := err;
  1373.     end; (* ICUCanInteract *)
  1374.  
  1375.         
  1376.     procedure SafeAppendMenu (menuH: MenuHandle; itemText: Str255);
  1377.         (* See comment in interface part. *)
  1378.     begin
  1379.         AppendMenu(menuH, 'fred');
  1380.  
  1381.         // If the string begins with a '-', we must change it
  1382.         // before calling SetMenuItemText because the system inteprets
  1383.         // a leading '-' as a disabled item, even in SetMenuItemText.
  1384.         // This code is WorldScript safe because the first byte of
  1385.         // the string is either a) the first byte of a two byte 
  1386.         // character, in which case it must be high bit set character,
  1387.         // and '-' isn't, b) a single byte character, in which case
  1388.         // the comparison makes sense because all script systems
  1389.         // contain Roman as the first 128 values of the one byte
  1390.         // characters.
  1391.  
  1392.         if (length(itemText) > 0) & (itemText[1] = '-') then begin
  1393.             itemText[1] := chr(0);
  1394.         end; (* if *)
  1395.  
  1396.         SetMenuItemText(menuH, CountMItems(menuH), itemText);
  1397.     end; (* SafeAppendMenu *)
  1398.  
  1399. end. (* ICCommonSubs *)
  1400.